/**
 * XML Conditional Functional Dependence
 * ----------------------------------------------------------------
 * 
 * This class represents a conditional functional dependence XML with the
 * transformation of path expressions in Finite automata. Thus we have:
 * 
 * xdf = (M, C, (T'-> T"))
 * 
 * where: M is the context path; C is the conditional path; T' is source path or
 * left side; T" is destination path or right side;
 * 
 * @author Eder P. C. Flor
 * @author Aryadne Guardieiro P. Rezende
 */
package model.constraints.XDFC;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import model.constraints.XDF;
import automata.Alphabet;
import automata.Fsa;
import automata.Label;
import automata.State;
import automata.StateSet;
import automata.Transitions;

public class XDFC extends XDF {

	private static final long serialVersionUID = 1L;
	
	private Fsa conditional = new Fsa();
	private String condicionalExpression;
	private Hashtable<State, ArrayList<XDFConditional>> statesRelation = new Hashtable<State, ArrayList<XDFConditional>>();
	private ArrayList<String> statesOperation = new ArrayList<String>();
	private String expRegCond = "^(([\\w]((\\s?/\\s?)|(\\s?//\\s?)*))*((\\s?)(!=|<=|>=|=|\\>|\\<)(\\s?)\'(\\w(\\s?))*\'*)((\\s?(AND)\\s?)|(\\s?(OR)\\s?)?))*$";
	private int conditionalPath = 1;//this variable define size array of conditional
	private Integer position = 0;//conta a quantidade de restricoes
	
	public XDFC() {}
	
	public XDFC(String name, Fsa f1, Fsa f2, Fsa f3, Fsa f4) {
		super(name, f1, f2, f3);
		conditional = f4;
	}
		
	public int indexOfStatesRelation(State s){		
		Iterator<Map.Entry<State, ArrayList<XDFConditional>>> it = statesRelation.entrySet().iterator();
		int i=0;
		
		while (it.hasNext()){
		  Entry<State,  ArrayList<XDFConditional>> entry = it.next();		  
		  if(entry.getKey() == s)
			return i;  			  
		  i++;
		}
		
		return -1;
	}
	
	public Fsa pathToFsaConditional(String path) throws Exception{
		path = path.trim();

		if(path.matches(expRegCond) == false)
			throw new Exception("Error on syntax of the regular expressions.");
		
		Hashtable<Label, Integer> states = new Hashtable<Label, Integer>();
		
		Label l = null;
		
		Fsa fsa = new Fsa();
		int i = 1;

		Label star = new Label("*");
		
		State start = new State(1);
		State sant;
		State s;		
		
		Alphabet alpha = new Alphabet();
		StateSet ss = new StateSet();
		StateSet fs = new StateSet();
		
		Transitions transitions = new Transitions();

		ss.add(start);
		sant = start;
				
		Expressions exp = new Expressions();		
		String valueRelational = null;//store the last value relational 
		
	    Pattern pattern = Pattern.compile("((!=|<=|>=|=|<|>|(/+))+\\s*)|([a-zA-Z0-9-_\'\\s]+\\s*)");
	    Matcher matcher = pattern.matcher(path);
	    ArrayList<String> list = new ArrayList<String>();
	    
	    while (matcher.find()) {
	    	String item = matcher.group().trim();
	    	if(!item.equals("") && item.contains("AND")){
	    		String[] result = item.split("AND");	    		
	    		list.add(result[0].trim());
	    		list.add("AND");
	    		list.add(result[1].trim());
	    	} else if(!item.equals("") && item.contains("OR")){
	    		String[] result = item.split("OR");
	    		list.add(result[0].trim());
	    		list.add("OR");
	    		list.add(result[1].trim());	    
	    	} else
	    		list.add(item.trim());	    
	    }
	    
		for(String item:list){						
			if (item.equals("|")){
				fs.add(sant);
				sant = start;
			} else if (item.equals("//")){
				alpha.add(star);
				StateSet ss2 = new StateSet();
				ss2.add(sant);
				transitions.put(sant, star, ss2);
			} else if (item.equals("AND") || item.equals("OR")){				
				fs.add(sant);
				sant = start;
				statesOperation.add(item.toLowerCase());						
			}	else if ((!item.equals("/")) && (!item.equals("//")) && (!item.equals("|")) ){				
				//check item if is relational store in valueRelation and wait the next value expected
				if(exp.checksRelational(item)){
					valueRelational = item;					
				} else if(item.startsWith("'") && item.endsWith("'")){
					createAutomata(item, valueRelational, sant, position);
					valueRelational = null;			
					position++;
				} else {																								
					l = new Label(item);
					if(states.containsKey(l)){
							sant = new State (states.get(l));
					} else {
						i++;	
						alpha.add(l);
						states.put(l, i);
						s = new State(i);
						ss.add(s);
						StateSet ss1 = new StateSet();
						ss1.add(s);
						transitions.put(sant, l, ss1);
						sant = s;
					}
					
				}
			}
    	}	    		
	   
		System.out.println(" states " + states.toString());
		fs.add(sant);		
		fsa.set_alphabet(alpha);
		fsa.set_states(ss);
		fsa.set_start_state(start);
		fsa.set_final_states(fs);
		fsa.set_transitions(transitions);
		
		return fsa;	    
	}
	
	public void createAutomata(String valueExpected, String valueRelational, State sant, Integer position){			

		System.err.println("\t\t state: " + sant + " - xdfconditiona " + valueExpected);
		if(!statesRelation.containsKey(sant)){
			ArrayList<XDFConditional> cond = new ArrayList<XDFConditional>();
			cond.add(new XDFConditional(valueExpected, valueRelational, position));
			statesRelation.put(sant, cond);
		} else {
			ArrayList<XDFConditional> cond = statesRelation.get(sant);
			cond.add(new XDFConditional(valueExpected, valueRelational, position));
			statesRelation.put(sant, cond);
		}
		 
		 
		valueExpected = null;
		valueRelational = null;
	}
	
	public void setEqualityRelation(){
		Iterator<Map.Entry<State,  ArrayList<XDFConditional>>> it = statesRelation.entrySet().iterator();
		
		while (it.hasNext()){
		  Entry<State,  ArrayList<XDFConditional>> entry = it.next();
		  	  
		  System.err.println("--------------------------------------------- ");		  
		  System.err.println(entry.getValue().toString());
		  System.err.println("States:" + entry.getKey());
		  System.err.println("--------------------------------------------- ");
		}
		
		System.out.println("VETOR DE OPERATIONS: " + statesOperation );
	}		

	/** Function that creates FSA (automaton) from a path
	 * @param path
	 * @return new FSA
	 */
	public Fsa getConditional() {
		return conditional;
	}

	public void setConditional(String conditional) throws Exception {
		this.condicionalExpression = new String(conditional);	
		this.conditional = pathToFsaConditional(conditional);		
		
		this.setEqualityRelation();
		
		System.out.println("conditional Path: " + conditionalPath);
		System.out.println("operation :" + statesOperation.toString());
	}
	
	public void setCondicionalExpression(String condicionalExpression) {
		this.condicionalExpression = condicionalExpression;
	}

	public String getCondicionalExpression() {
		return condicionalExpression;
	}	

	/**
	 * from fsa we know which type of contextFsa, sourceFsa, targetFsa or conditional 
	 * @param Object FSA
	 * @return integer 1 is contextFSA, 2 source FSA or 3 targetFSA
	 */
	public int getFsaPosition(Fsa f) {
		if (f == contextFsa) 
			return 1;
		if (f == sourceFsa)
			return 2;
		if (f == targetFsa)
			return 3;		
		if (f == conditional)
			return 4;
		else
			return 0;
	}

	public Hashtable<State,  ArrayList<XDFConditional>> getStatesRelation() {
		return statesRelation;
	}

	public void setStatesRelation(Hashtable<State,  ArrayList<XDFConditional>> statesRelation) {
		this.statesRelation = statesRelation;
	}
	
	public void setExpRegCond(String expRegCond) {
		this.expRegCond = expRegCond;
	}

	public String getExpRegCond() {
		return expRegCond;
	}

	public void setConditionalPath(int conditionalPath) {
		this.conditionalPath = conditionalPath;
	}

	public int getConditionalPath() {
		return conditionalPath;
	}	

	public void setStatesOperation(ArrayList<String> statesOperation) {
		this.statesOperation = statesOperation;
	}

	public ArrayList<String> getStatesOperation() {
		return statesOperation;
	}	
	
	public Integer getCountConstraint(){
		return this.position;
	}
	
	public String toString() {
		String str = new String();
		str = "<b>XDFC Name: </b>" + nameConstraint + "<br><br>";
		str += "<b>Fsa Context: </b>" + contextFsa.toStringWeb() + "<br>";
		str += "<b>Fsa Source: </b>" + sourceFsa.toStringWeb() + "<br>";
		str += "<b>Fsa Target: </b>" + targetFsa.toStringWeb() + "<br>";
		str += "<b>Fsa Conditional: </b>" + conditional.toStringWeb() + "<br>";
		str += "<b>Paths: </b>" + npaths + "<br>";
		return str;
	}
	
	public String toStringPath() {
		String str = new String();
		str += "<b>XDFC Name:</b> " + nameConstraint + "<br><br>";
		str += "<b>Constraint name:</b> " + context + "<br><br>";
		str += "<b>Left Path:</b> " + leftPath +  "<br><br>";
		str += "<b>Type of equality Left:</b> " + leftValue + "<br><br>";
		str += "<b>Right Path:</b> " + rightPath +  "<br><br>";
		str += "<b>Conditional: </b>" + condicionalExpression + "<br><br>";
		
 		if(rightValue == 0)
			str += "<b>Type of equality Right:</b> Value<br>";
		else
			str += "<b>Type of equality Right:</b> Node<br>";
		
		return str;
	}
}